#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <memory>
#include <functional>
#include "thread.hpp"
#include "cond.hpp"
#include "mutex.hpp"
#include "log.hpp"
#include <mysql/mysql.h>

#define NUM 5

const char* host = "127.0.0.1";
const char* user = "bb";
const char* password = "123456";
const char* db = "test";
const unsigned int port = 3306;

namespace ThreadPoolModule
{
    using namespace MyThread;
    using namespace MutexModule;
    using namespace CondModule;
    using namespace LogModule;

    class MysqlOperator
    {
    public:
        MysqlOperator()
        {}
        void InitMysql()
        {
            _mysql = mysql_init(nullptr);  // 初始化这个数据库
            if(_mysql == nullptr)
            {
                LOG(LEVEL::DEBUG) << "初始化数据库失败";
                return;
            }
            if(mysql_real_connect(_mysql, host, user, password, db, port, nullptr, 0) == nullptr)
            {
                LOG(LEVEL::DEBUG) << "数据库连接失败";
                return;
            }
        }
        void ExcuteInstruction(const std::string& instruction)
        {
            int tem = mysql_query(_mysql, instruction.c_str());
            if(tem != 0)
            {
                LOG(LEVEL::DEBUG) << "指令执行失败" << mysql_error(_mysql);
                return;
            }
            LOG(LEVEL::DEBUG) << "指令执行成功";
        }
        ~MysqlOperator()
        {
            mysql_close(_mysql);
            LOG(LEVEL::DEBUG) << "数据库关闭完成";
        }
    private:
        MYSQL* _mysql;
    };

    template <class T> // 任务类型由用户自己传入
    class threadpool
    {
    private:
        bool IsEmpty() { return _tasks.empty(); }
        void Handler()
        {
            while (true)
            {

                // handler 函数要做的就是拿到任务 处理任务
                T t;
                {
                    Lockguard lockguard(_mtx);
                    // 拿到任务
                    // 这里的循环判断是为了 _isrunning 做准备 如果线程被唤醒之后 它的标志位为false
                    // 那他就退出循环 去执行下面if语句 如果if语句不满足就退出
                    // 如果线程池中仍然有线程就继续执行任务 下一次执行任务之后 然后在退出
                    while (IsEmpty() && _isrunning)
                    {
                        w_thread++;
                        // 任务队列是空的 就要等待任务进队列
                        _cond.wait(_mtx);
                        w_thread--;
                    }

                    if (IsEmpty() && !_isrunning)
                    {
                        // 退出线程池 因为 isrunning 被设置为 false 并且线程池中再没有任务
                        break;
                    }

                    // 拿到任务了
                    t = _tasks.front();
                    _tasks.pop();
                }

                // 处理任务不需要加锁的保护
                // 这里规定 任务的类型不需要传入参数
                _mysql->ExcuteInstruction(t);
            }
        }

    public:
        threadpool()
            : w_thread(0)
            , _isrunning(false)
            , _mysql(std::make_unique<MysqlOperator>())
        {
            for (int i = 0; i < NUM; i++)
            {
                // 创建所有线程池中的执行任务的线程
                // 这里用到绑定可以让这个函数的参数只有一个 和 func_t 类型的函数保持一致
                _threads.push_back(std::make_shared<thread>(std::bind(&threadpool::Handler, this)));
            }
        }

        void Start()
        {
            if (_isrunning == true)
                return;
            {
                Lockguard lockguard(_mtx);
                _mysql->InitMysql();
                _isrunning = true;
                // 启动所有线程池中的线程
                for (auto &e : _threads)
                {
                    e->start();
                }
            }
        }

        void Equeue(T &&in)
        {
            if (_isrunning == false)
                return;
            // 给队列中放入任务
            {
                Lockguard lockguard(_mtx);
                _tasks.push(std::move(in));

                if (w_thread > 0)
                    _cond.notify();
            }
        }

        void Stop()
        {
            if (_isrunning)
            {
                {
                    Lockguard lockguard(_mtx);
                    // 停止线程池之后 任务队列不能在入任务
                    // 线程池中的线程也应该被结束
                    _isrunning = false;

                    // 唤醒所有历史的线程 没有任务就会直接退出 有任务就会执行完在退出
                    _cond.allnotify();
                }
            }
        }

        void Join()
        {
            // 回收所有进程
            if (!_isrunning)
            {
                for (auto &e : _threads)
                {
                    e->join();
                    std::cout << "回收一个线程" << std::endl;
                }
            }
        }

        ~threadpool() 
        {
            Join();
        }

    private:
        // 这里用到队列和顺序表 代表对任务队列 和 线程池中线程的先组织在描述
        std::vector<std::shared_ptr<thread>> _threads;
        std::queue<T> _tasks;
        std::unique_ptr<MysqlOperator> _mysql;
        bool _isrunning;
        int w_thread;
        mutex _mtx;
        cond _cond;
    };
}